// --------------------------------------------------------------------------------------------------------------------- 
// <copyright file="GlobalHelpers.cs" company="Scrum for Team System">
//   None
// </copyright>
// <summary>
//   Defines the GlobalHelpers type.
// </summary>
// ---------------------------------------------------------------------------------------------------------------------
namespace ScrumForTeamSystem.EventService.Infrastructure
{
    #region

    using System;
    using System.Globalization;
    using System.Linq;
    using System.Text;

    using Interfaces;

    using Microsoft.TeamFoundation.WorkItemTracking.Client;

    #endregion

    /// <summary>
    /// Defines the GlobalHelpers type.
    /// </summary>
    public static class GlobalHelpers
    {
        /// <summary>
        /// Determines whether [the specified work item changed event] [has changed relations].
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        /// <returns>
        /// <c>True</c> if [the specified work item changed event] [has changed relations]; otherwise, <c>false</c>.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated in assert method")]
        public static bool HasLinkChanges(this WorkItemChangedEvent workItemChangedEvent)
        {
            AssertEventIsNotNull(workItemChangedEvent); 
            
            return (workItemChangedEvent.AddedRelations != null && workItemChangedEvent.AddedRelations.Length != 0)
                   ||
                   (workItemChangedEvent.ChangedRelations != null && workItemChangedEvent.ChangedRelations.Length != 0)
                   ||
                   workItemChangedEvent.HasDeletedLinkChanges();
        }

        /// <summary>
        /// Determines whether [the specified work item changed event] [has deleted links].
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        /// <returns>
        /// <c>true</c> if [the specified work item changed event] [has deleted links]; otherwise, <c>false</c>.
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated in assert method")]
        public static bool HasDeletedLinkChanges(this WorkItemChangedEvent workItemChangedEvent)
        {
            AssertEventIsNotNull(workItemChangedEvent); 
            return workItemChangedEvent.DeletedRelations != null && workItemChangedEvent.DeletedRelations.Length != 0;
        }

        /// <summary>
        /// Get all the changed link ids.
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        /// <returns>An array of all the linked ids that have been altered.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated in assert method")]
        public static int[] AllChangedLinkIds(this WorkItemChangedEvent workItemChangedEvent)
        {
            AssertEventIsNotNull(workItemChangedEvent);

            var deletedIds = workItemChangedEvent.DeletedRelations == null
                                 ? new int[] { }
                                 : workItemChangedEvent.DeletedRelations.Select(r => int.Parse(r.WorkItemId, CultureInfo.InvariantCulture));

            var addedIds = workItemChangedEvent.AddedRelations == null
                               ? new int[] { }
                               : workItemChangedEvent.AddedRelations.Select(r => int.Parse(r.WorkItemId, CultureInfo.InvariantCulture));

            return deletedIds.Union(addedIds).ToArray();
        }

        /// <summary>
        /// Gets the core field int.
        /// </summary>
        /// <param name="workItemChangedEvent">The wic event.</param>
        /// <param name="coreFieldName">The core field name.</param>
        /// <returns>
        /// The integer value of the core field specified
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated in assert method")]
        public static int GetWholeNumberFieldValue(this WorkItemChangedEvent workItemChangedEvent, string coreFieldName)
        {
            AssertEventIsNotNull(workItemChangedEvent);

            if (!IsValidObject(workItemChangedEvent))
            {
                return -1;
            }

            var value = workItemChangedEvent.CoreFields.IntegerFields
                            .Where(f => f.ReferenceName.Equals(coreFieldName)).Select(f => f.NewValue).ToArray();

            return value.Count() != 0 ? value.First() : -1;
        }

        /// <summary>
        /// Gets the core field string.
        /// </summary>
        /// <param name="workItemChangedEvent">The wic event.</param>
        /// <param name="coreFieldName">The core field name.</param>
        /// <returns>
        /// The string value of the core field specified
        /// </returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", Justification = "Validated in assert method")]
        public static string GetStringFieldValue(this WorkItemChangedEvent workItemChangedEvent, string coreFieldName)
        {
            AssertEventIsNotNull(workItemChangedEvent);

            if (!IsValidObject(workItemChangedEvent))
            {
                return null;
            }

            return workItemChangedEvent.CoreFields.StringFields
                            .Where(f => f.ReferenceName.Equals(coreFieldName))
                            .Select(f => f.NewValue).FirstOrDefault();
        }

        /// <summary>
        /// Tries to get the event source as a work item changed event.
        /// </summary>
        /// <typeparam name="T">The event type.</typeparam>
        /// <param name="eventInstance">The event instance.</param>
        /// <param name="eventObject">The event object.</param>
        /// <returns>
        /// <c>True</c> if the event source is of specifeid type; otherwise <c>false</c>.
        /// </returns>
        public static bool TryCastEvent<T>(ITfsEvent eventInstance, out T eventObject) where T : class
        {
            if (eventInstance == null)
            {
                throw new ArgumentNullException("eventInstance");
            }

            eventObject = eventInstance.EventSource as T;

            return eventObject != null;
        }

        /// <summary>
        /// Applies the time stamp.
        /// </summary>
        /// <param name="targetWorkItem">The target work item.</param>
        /// <param name="timestampField">The time stamp field.</param>
        /// <returns>The work item with time stamp applied.</returns>
        public static WorkItem ApplyTimestamp(WorkItem targetWorkItem, string timestampField)
        {
            if (targetWorkItem == null)
            {
                throw new ArgumentNullException("targetWorkItem");
            }

            var targetField =
                    targetWorkItem.Fields.OfType<Field>().FirstOrDefault(
                            f => f.ReferenceName.Equals(timestampField));

            if (targetField != null)
            {
                targetField.Value = DateTime.Now;
            }

            return targetWorkItem;
        }

        /// <summary>
        /// Validates the work workItem.
        /// </summary>
        /// <param name="workItem">The work item.</param>
        /// <returns>The validated work workItem.</returns>
        /// <exception cref="ArgumentException"></exception>
        public static WorkItem ValidateWorkItem(WorkItem workItem)
        {
            if (workItem == null)
            {
                throw new ArgumentNullException("workItem");
            }

            var validationErrors = workItem.Validate();

            if (validationErrors != null && validationErrors.Count != 0)
            {
                var sb = new StringBuilder();

                var message = string.Format(
                    CultureInfo.InvariantCulture,
                    "*** WORK ITEM ERROR *** Validation errors found in work workItem {0}.",
                    workItem.Id);

                sb.Append(message);

                foreach (var error in validationErrors.OfType<Field>())
                {
                    message = string.Format(
                        CultureInfo.InvariantCulture,
                        "Field: '{0}' status: '{1}' value: '{2}'",
                        error.ReferenceName,
                        error.Status,
                        error.Value);

                    sb.Append("\r\n");
                    sb.Append(message);
                }

                throw new ArgumentException(string.Concat("The work workItem is not valid.\r\n", sb));
            }

            return workItem;
        }

        /// <summary>
        /// Determines whether [the specified wic event] [is valid object].
        /// </summary>
        /// <param name="wicEvent">The wic event.</param>
        /// <returns>
        /// <c>true</c> if [the specified wic event] [is valid object]; otherwise, <c>false</c>.
        /// </returns>
        private static bool IsValidObject(WorkItemChangedEvent wicEvent)
        {
            return wicEvent.CoreFields != null;
        }

        /// <summary>
        /// Asserts the event is not null.
        /// </summary>
        /// <param name="workItemChangedEvent">The work item changed event.</param>
        private static void AssertEventIsNotNull(WorkItemChangedEvent workItemChangedEvent)
        {
            if (workItemChangedEvent == null)
            {
                throw new ArgumentNullException("workItemChangedEvent");
            }
        }
    }
}